#[cfg(test)]
mod test {
    use std::thread;
    fn f(){
        println!("Hello from another thread!");
        let id = thread::current().id();
        println!("this is my thread id: {id:?}");
    }
    #[test]
    fn start_2thread(){
        thread::spawn(f);
        thread::spawn(f);
        println!("hello from the main thread.");
    }
    #[test]
    fn join_thread() {
        let t1 = thread::spawn(f);
        let t2 = thread::spawn(f);
        println!("hello from the main thread.");

        t1.join().unwrap();
        t2.join().unwrap();
    }

    // move data to thread 
    #[test]
    fn join_for_result(){
        let numbers = Vec::from_iter(0..=1000);
        let t = thread::spawn(move || { // move for numbers
            let len = numbers.len();
            let sum = numbers.iter().sum::<usize>();
            sum / len 
        });

        let average = t.join().unwrap();
        println!("average: {average}");
    }

    // share data between threads using scope
    #[test]
    fn scope() {
        let numbers = vec![1, 2, 3];
        thread::scope(|s| {
            s.spawn(||{
                println!("length: {}", numbers.len());
            });
            s.spawn(||{
                for n in &numbers {
                    println!("{n}");
                }
            });
        });
    }

    #[test]
    fn shared_static() {
        static X: [i32; 3] = [1, 2, 3];
        thread::spawn(|| dbg!(&X));
        thread::spawn(|| dbg!(&X));
    }
    #[test]
    fn shared_with_leak(){
        // x is ownner by current function : x is ref var  to global value
        let x: &'static [i32; 3] = Box::leak(Box::new([1, 2, 3]));
        //x is a ref var, so is copyable when move 
        thread::spawn(move || dbg!(x));
        thread::spawn(move || dbg!(x));
    }

    use std::rc::Rc;
    #[test]
    fn rc_use(){
        let a = Rc::new([1, 2, 3]);
        let b = Rc::clone(&a);
        assert_eq!(a.as_ptr(), b.as_ptr());
    }
    use std::sync::Arc;
    #[test]
    fn rc_use_in_thread() {
        {
            let a = Arc::new([1, 2, 3]);
            let b = Arc::clone(&a);

            thread::spawn(move || dbg!(a)).join().unwrap();
            thread::spawn(move || dbg!(b)).join().unwrap();
        }
        { //better use  with same name in sub-thread
            let a = Arc::new([1, 2 ,3]);

            thread::spawn({
                let a = Arc::clone(&a); 
                move || {
                dbg!(a);
                //a.sort()  //compile error! because shared with readonly
                }
            });

            dbg!(a);
        }
    }
    use std::cell::{Cell, RefCell};
    #[test]
    fn interior_mutable(){
        let c = Cell::new(Vec::<u8>::new()); //Atomics type similar with Cell
        let a = &c;
        let b = &c;
        
        let mut v = a.take();
        v.push(1);
        b.set(v);

        assert_eq!(a.as_ptr(), b.as_ptr());


        let c = RefCell::new(Vec::<u8>::new()); //RwLock similar with RefCell
        c.borrow_mut().push(1);
    }

    use std::sync::Mutex;
    #[test]
    fn mutex_locks_scope(){
        let n = Mutex::new(0);

        thread::scope(|s| { 
            for _ in 0..10 {
                s.spawn(|| {
                    let mut guard = n.lock().unwrap();
                    for _ in 0..100 {
                        *guard += 1;
                    }
                });
            }
        });

        assert_eq!(n.into_inner().unwrap(), 1000);
    }
    #[test]
    fn mutex_locks_no_scope(){
        let n = Arc::new(Mutex::new(0));

        let mut ts = Vec::new();
        for _ in 0..10 {
            ts.push(thread::spawn({
                let n = Arc::clone(&n);
                move || {
                    let mut guard = n.lock().unwrap();
                    for _ in 0..100 {
                        *guard += 1;
                    }
                }
            }));
        }
        for t in ts {
            t.join().unwrap();
        }

        assert!(*n.lock().unwrap() == 1000);
    }
    
    // using  lock_guard 
    //1. lock one line: list.lock().unwrap().push(1) 
    //
    //2. lock one statement
    //  if/while let Some(s) = list.lock().unwrap().pop() {
    //  .... // locked ....
    //  } //unlocked
    //2.better
    //  let item = list.lock().unwrap().pop();
    //  if let Some(item) = item {
    //      process(item);
    //  }


    use std::collections::VecDeque;
    use std::time::Duration;
    #[test]
    fn use_park(){
        let queue = Mutex::new(VecDeque::new());

        thread::scope(|s| {
            let t = s.spawn(|| 
                for _ in 0..10 {
                //loop {
                    let item = queue.lock().unwrap().pop_front();
                    if let Some(item) = item {
                        println!("{item}");
                    } else {
                        thread::park();
                    }
                });

            for i in 0..10 {
            //for i in 0.. {
                queue.lock().unwrap().push_back(i);
                t.thread().unpark();
                thread::sleep(Duration::from_millis(10));
            }
        });
    }
    use std::sync::Condvar;
    #[test]
    fn use_con_var(){
        let queue = Mutex::new(VecDeque::new());
        let not_empty = Condvar::new();

        thread::scope(|s|{
            //consumer
            s.spawn(|| {
                for _ in 0..10 {
                //loop {
                    let mut q_lock_guard = queue.lock().unwrap();
                    let item = loop {
                        if let Some(item) = q_lock_guard.pop_front() {
                            break item;
                        }
                        else{
                            q_lock_guard = not_empty.wait(q_lock_guard).unwrap();
                        }
                    };
                    drop(q_lock_guard);
                    println!("{item}");
                }
            });//spawn new thread

            //producer
            for i in 0..10 {
                queue.lock().unwrap().push_back(i);
                not_empty.notify_one();
                thread::sleep(Duration::from_millis(10));
            }

        });//thread::scope
    }
}
